const JSZip = require('jszip');
const util = require('./utils');
var MidiWriter = require('midi-writer-js');

//歌谱和midi
class CCMZ {
  ver;
  score;
  midi;
}

const libCCMZ = {
  //下载CCMZ
  downloadCCMZ(url) {
    return util.httpget(url, '', true, '琴谱文件', false);
  },

  //解析CCMZ文件，来自Controller.js
  readCCMZ(buffer,callback) {
    let info = new CCMZ();
    let version = (new Uint8Array(buffer.slice(0, 1)))[0];
    console.log("CCMZ版本:", version);
    info.ver = version;
    let data = new Uint8Array(buffer.slice(1))
    if (version == 1) {
      JSZip.loadAsync(data).then((zip) => {
        zip
          .file("data.xml")
          .async("string")
          .then((json) => {
            info.score = json;
            zip
              .file("data.mid")
              .async("string")
              .then((json) => {
                info.midi = json;
                callback(info);
              });
          });
      });
    } else if (version == 2) {
      data = data.map((value) => {
        return value % 2 == 0 ? value + 1 : value - 1
      })
      JSZip.loadAsync(data).then((zip) => {
        zip
          .file("score.json")
          .async("string")
          .then((json) => {
            info.score = json;
            zip
              .file("midi.json")
              .async("string")
              .then((json) => {
                info.midi = json;
                callback(info);
              });
          });
      });
    }
  },

  //转换为MID文件
  writeMIDI(input,outputFile) {
    const baseMultiplier = 0.25;
    //速度tick tempo
    let tempoTicks = new Array();
    for(tpo in input['tempos']) {
      var tick = input['tempos'][parseInt(tpo)]['tick'];
      tempoTicks.push(tick);
    }
    const baseTempo = input['tempos'][0]['tempo'];

    //初始化音轨
    var tracks = new Array();
    for (t in input['tracks']) {
      let trackOrig = input['tracks'][parseInt(t)];
      let currTrack = new MidiWriter.Track();
      //水印，可以不加
      currTrack.addText('Generated by chongchong-free');
      //乐器是钢琴
      currTrack.addEvent(new MidiWriter.ProgramChangeEvent({instrument: 1}));
      //名字
      let name = trackOrig['name'] != '' ? trackOrig['name'] : 'Unamned';
      currTrack.addTrackName(name);
      //基本速度
      currTrack.setTempo(Math.round(60000000 / baseTempo), 0);
      
      let globalOffset = 0;

      //添加note
      for(ev in input['events']) {
        let event = input['events'][parseInt(ev)];
        //删除不必要音符
        if (event['duration'] == 0 || !event['staff']) {
          event['validEvent'] = false;
          continue;
        } else {
          event['validEvent'] = true;
        }

        //0tick问题
        if (event['tick'] == 0) {
          event['tick'] = 1;
        } 

        //上一个event
        let lastValidEvent = event;
        for (let _ev = parseInt(ev); _ev > 0; _ev--) {
          if (input['events'][_ev-1]['validEvent'] == true) {
            lastValidEvent = input['events'][_ev-1];
            break;
          }
        }


        tempoTicks.push(event['tick']);
        tempoTicks.sort((o, j) => {
          return o - j;
        });
        let tickPos = tempoTicks.indexOf(event['tick']);
        let currTempo = baseTempo;
        let mDuration, mTick = 1;
        if (tickPos != 0) {
          let thisTick = tempoTicks[tickPos-1];
          let lastTempo = baseTempo;
          for(tpo in input['tempos']) {
            let _tpo = parseInt(tpo);
            var tick = input['tempos'][_tpo]['tick'];
            if (_tpo != 0) {
              lastTempo = input['tempos'][_tpo-1]['tempo'];
            }
            currTempo = input['tempos'][_tpo]['tempo'];
            if (tick === thisTick) {
              mDuration = baseMultiplier * lastTempo / currTempo;
              mTick = baseMultiplier * lastTempo / currTempo;
            } else break;
          }
        }

        tempoTicks.splice(tickPos, 1);
        let tickDelta = event['tick'] - lastValidEvent['tick'];
        globalOffset += tickDelta * mTick;
        if (event['tick'] + globalOffset < 0) globalOffset = 0;
        let note = new MidiWriter.NoteEvent({
          velocity: 80,
          pitch: [event['event'][1]],
          duration: "T" + event['duration'] * mDuration,
          startTick: globalOffset,//globadOffset
        });


        let trackID = event['staff'] - 1;
        if (parseInt(t) == trackID) {
          currTrack.addEvent(note);
        }
      }
      tracks.push(currTrack);
    }
    //写出
    if (util.isDetailedOutput()) console.log('开始写出MIDI文件');
    let write = new MidiWriter.Writer(tracks);
    require('fs').writeFileSync(outputFile, write.buildFile());
  }
}

module.exports = libCCMZ;

//MIDI类

class MeasureInfo {
  beats;//拍子
  number;//序号
  fifths;//升降调
  beatUnit;//拍子
}

class Event {
  tick;//时间
  duration;//持续时长
  track;//音轨序号
  event;//按键
  finger;//手指
  note;//未知
  part;//未知
  repeatIndex;//重复次数
  staff;//未知
  measure;//所在小节号
  elem_ids;//未知
  meas_start_tick;//小节开始时间
  id;//未知
}

class Measure {
  duration;//小节时长
  note_ticks;//每个音符出现时间
  measure;//小节号
}

class TempoChange {
  tempo;//速度
  tick;//时间
}

class Track {
  channel;//未知
  name;//名称
  program;//未知
}

class BeatChange {
  beats;//拍子
  beatsUnit;//拍子
  tick;//时间
}

class MIDI {
  ver;//版本号
  leftHandTrack;//低音音轨
  rightHandTrack;//高音音轨
  roughProgress;//未知
  beats;//拍号
  beatsUnit;//拍号
  beatInfos;//变拍子
  tempos;//变速
  tracks;//音轨
  measures;//小节
  measureInfos;//小节信息
  lyrics;//歌词
  events;//midi的event
}